#include "StdAfx.h"
#include <cassert>
#include "AdderBytecodeEngine.h"
#include "AdderObject.h"
#include "AdderVariableScoper.h"
#include "AdderFunction.h"
#include "AdderEngineMacros.h"
#include "AdderBuiltinFunction.h"
#include <iostream>
#include <math.h>
#pragma warning (disable : 4482)

#ifdef VALUE_ALLOC_DEBUG
long int valuesAllocated;
#endif

AdderValue::AdderValue()
{
	this->type = AdderValueType::UNSET;
	this->b_array = false;
	this->o_array = true; // are / will we be the owner of the array?
	this->b_string = false;
	this->r_array = NULL;
	this->r_number = 0;
	this->r_string = NULL;
	this->r_object = NULL;
#ifdef VALUE_ALLOC_DEBUG
	valuesAllocated += 1;
	printf("NEW      [VAL:   NULL   -> %p (%i)\n", this, valuesAllocated);
#endif
}

AdderValue::AdderValue(const AdderValue& c)
{
	this->type = c.type;
	this->b_array = c.b_array;
	this->o_array = false; // are / will we be the owner of the array?
	this->b_string = c.b_string;
	this->r_array = c.r_array;
	this->r_number = c.r_number;
	if (this->b_string)
	{
		this->r_string = new AdderString(*(c.r_string)); // clone
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1; // This string has been tracked (assuming our memory
							   // allocation issue is with lost pointers to strings
						       // and not values).
#endif
	}
	else
	{
		this->r_string = NULL;
	}
	this->r_object = c.r_object;
#ifdef VALUE_ALLOC_DEBUG
	valuesAllocated += 1;
	printf("COPY     [VAL: %p -> %p (%i)\n", &c, this, valuesAllocated);
#endif
}

AdderValue& AdderValue::operator =(const AdderValue& c)
{
	this->type = c.type;
	this->b_array = c.b_array;
	this->o_array = false; // are / will we be the owner of the array?
	this->b_string = c.b_string;
	this->r_array = c.r_array;
	this->r_number = c.r_number;
	if (this->b_string)
	{
		this->r_string = new AdderString(*(c.r_string)); // clone
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1; // This string has been tracked (assuming our memory
							   // allocation issue is with lost pointers to strings
						       // and not values).
#endif
	}
	else
	{
		this->r_string = NULL;
	}
	this->r_object = c.r_object;
#ifdef VALUE_ALLOC_DEBUG
	valuesAllocated += 1;
	printf("ASSIGN   [VAL: %p -> %p (%i)\n", &c, this, valuesAllocated);
#endif
	return (*this);
}

AdderValue::~AdderValue()
{
	if (this->b_array && this->o_array)
	{
		for (std::map<float, AdderValue*>::const_iterator iter = this->r_array->begin();
				iter != this->r_array->end(); ++iter)
		{
			delete iter->second;
		}
		delete this->r_array;
	}
	if (this->b_string)
	{
		delete this->r_string;
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated += 1; // offset again
#endif
	}
#ifdef VALUE_ALLOC_DEBUG
	valuesAllocated -= 1;
	printf("FREE (D) [VAL: %p ->   NULL   (%i)\n", this, valuesAllocated);
#endif
}

std::map<float, AdderValue*>*& AdderValue::a_array()
{
	if (!this->b_array)
	{
		this->r_array = new std::map<float, AdderValue*>();
		this->b_array = true;
	}
	
	return this->r_array;
}

AdderString& AdderValue::a_string(bool replacement)
{
	if (!this->b_string)
	{
		// replacement indicates that the calling code
		// intendes to completely replace the r_string
		// value, therefore we shouldn't allocate one
		// here.
		if (!replacement)
			this->r_string = new AdderString();
#ifdef STRING_ALLOC_DEBUG
		stringsAllocated -= 1; // This string has been tracked (assuming our memory
							   // allocation issue is with lost pointers to strings
							   // and not values).
#endif
		this->b_string = true;
	}
	
	return (*(this->r_string));
}

AdderBytecodeEngine::AdderBytecodeEngine(unsigned int variable_scope_size, unsigned int register_scope_size)
{
	this->debug = false;

	this->scope_level = 0;
	this->assembly_jump_required = false;
	this->assembly_jump_class_position = NULL;
	
	this->return_scope = NULL;

	this->breaking = false;
	this->breaking_label = "";

	this->breaking_catching = false;
	this->breaking_catchlabel = "";
	this->breaking_catchexcp = NULL;
	this->breaking_catchregister = -1;
	
	this->catch_errorlevel = 0;

	// Set register and variable erasure keys.
	this->registers = new AdderVariableScoper(register_scope_size);
	this->variables = new AdderVariableScoper(variable_scope_size);

	// Hardcoding builtin variables.
	/*

	TODO: Provide builtin libraries some way of giving the compiler a list
		  of reserved variable slots so that they can be used correctly.

	*/
}

AdderBytecodeEngine::~AdderBytecodeEngine(void)
{
	delete this->registers;
	delete this->variables;
	
	// We need to delete each of the function and class objects
	// in the this->functions and this->classes maps.
	for (std::map<AdderString, AdderFunction*>::const_iterator iter = this->functions.begin();
			iter != this->functions.end(); ++iter)
	{
		delete iter->second;
	}
	for (std::map<AdderString, AdderObject*>::const_iterator iter = this->classes.begin();
			iter != this->classes.end(); ++iter)
	{
		delete iter->second;
	}
}

// Builtin Initalization
void AdderBytecodeEngine::BuiltinLoadLibrary(BuiltinInitalizer func)
{
	func(this);
}

void AdderBytecodeEngine::BuiltinRegisterScopeChange(AdderString name, BuiltinScopeChangeFunction func)
{
	this->builtins_scope_reload_functions.insert(string_scopefunction_map(name, func));
}

void AdderBytecodeEngine::BuiltinRegisterFunction(AdderString name, BuiltinFunction0 func)
{
	this->functions.insert(string_function_map(name, new BuiltinFunction(func)));
}

void AdderBytecodeEngine::BuiltinRegisterFunction(AdderString name, BuiltinFunction1 func)
{
	this->functions.insert(string_function_map(name, new BuiltinFunction(func)));
}

void AdderBytecodeEngine::BuiltinRegisterFunction(AdderString name, BuiltinFunction2 func)
{
	this->functions.insert(string_function_map(name, new BuiltinFunction(func)));
}

void AdderBytecodeEngine::BuiltinRegisterFunction(AdderString name, BuiltinFunction3 func)
{
	this->functions.insert(string_function_map(name, new BuiltinFunction(func)));
}

void AdderBytecodeEngine::BuiltinRegisterFunction(AdderString name, BuiltinFunction4 func)
{
	this->functions.insert(string_function_map(name, new BuiltinFunction(func)));
}

void AdderBytecodeEngine::BuiltinRegisterFunction(AdderString name, BuiltinFunction5 func)
{
	this->functions.insert(string_function_map(name, new BuiltinFunction(func)));
}

void AdderBytecodeEngine::BuiltinRegisterClass(AdderString name, AdderObject* class_def) {}
void AdderBytecodeEngine::BuiltinRegisterVariable(AdderString name, AdderValue* var) {}

// User-defined Functions / Classes Registration
void AdderBytecodeEngine::UserRegisterFunction(AdderString name, std::vector<unsigned int> arguments)
{
	AdderFunction * f = new AdderFunction();
	f->argument_count = arguments.size();
	f->argument_names = arguments;
	this->functions.insert(string_function_map(name, f));
}

void AdderBytecodeEngine::UserRegisterClassFunction(AdderString name, AdderString clsname, void * jmp, std::vector<unsigned int> arguments)
{
	if (this->_class_defined(clsname.c_str()))
	{
		AdderFunction * f = new AdderFunction();
		f->argument_count = arguments.size();
		f->argument_names = arguments;
		f->jump_location = jmp;
		this->_class_get(clsname.c_str())->functions.insert(string_function_map(name, f));
	}
	else
		assert(false);
}

void AdderBytecodeEngine::ScopeCleanup()
{
	// We need to reset all of the registers and variables back correctly
	// after our scope change.
	this->registers->lower();
	this->variables->lower();
	this->scope_level -= 1;
}

// Bytecode: Register Creation
void AdderBytecodeEngine::NEWC	(int reg, const char* cls)
{
	BREAK_CHECK_2("NEWC", reg, cls);

	// Check if the class is defined.
	if (!this->_class_defined(cls))
	{
		THROW_ERROR_2("NEWC", reg, cls, "Undefined class '" << cls << "'.");
		return;
	}

	AdderObject * o = this->_class_get(cls);
	AdderValue * t = this->_register_new(reg);
	t->type = AdderValueType::OBJECT;
	t->r_object = o;

	DEBUG_BYTECODE_2("NEWC", reg, cls);
}

void AdderBytecodeEngine::NEWA	(int reg)
{
	BREAK_CHECK_1("NEWA", reg);

	AdderValue * t = this->_register_new(reg);
	t->type = AdderValueType::ARRAY;

	DEBUG_BYTECODE_1("NEWA", reg);
}

void AdderBytecodeEngine::NEWS	(int reg, const char* str)
{
	BREAK_CHECK_2("NEWS", reg, str);

	AdderString strstr = str;

	if (strstr.at(0) == '"' && strstr.at(strstr.size()-1) == '"')
	{
		// We're dealing with a string literal.
		AdderValue * t = this->_register_new(reg);
		t->type = AdderValueType::STRING;
		t->a_string() = strstr.substr(1, strstr.size() - 2).get();
	}
	else
	{
		AdderValue * t = this->_register_new(reg);
		t->type = AdderValueType::STRING;
		t->a_string() = AdderString(str);
	}

	DEBUG_BYTECODE_2("NEWS", reg, str);
}

void AdderBytecodeEngine::NEWN	(int reg, float number)
{
	BREAK_CHECK_2("NEWN", reg, number);

	AdderValue * t = this->_register_new(reg);
	t->type = AdderValueType::NUMBER;
	t->r_number = number;

	DEBUG_BYTECODE_2("NEWN", reg, number);
}

// Bytecode: Register / Variable Movement and Basic Comparisons
void AdderBytecodeEngine::RMOVV	(int reg, int var)
{
	BREAK_CHECK_2("RMOVV", reg, var);

	if (this->_register_exists(reg))
	{
		THROW_ERROR_2("RMOVV", reg, var, "Register '" << reg << "' is already defined, therefore, can not copy value to this register.");
		return;
	}

	if (!this->_variable_exists(var))
	{
		THROW_ERROR_2("RMOVV", reg, var, "Undefined variable '" << var << "'.");
		return;
	}

	AdderValue * v = this->_variable_get(var);
	AdderValue * r = this->_register_new(reg);

	// Check and make sure we have valid values.
	assert(v != NULL && r != NULL);

	// Set the registers's type.
	r->type = v->type;
	
	switch (v->type)
	{
		case AdderValueType::NUMBER:
			r->r_number = v->r_number;
			break;
		case AdderValueType::STRING:
			r->a_string() = v->a_string().c_str();
			break;
		case AdderValueType::ARRAY:
			r->a_array() = v->a_array();
			break;
		case AdderValueType::OBJECT:
			r->r_object = v->r_object;
			break;
		default:
			THROW_ERROR_2("RMOVV", reg, var, "Type of variable '" << var << "' is unknown.");
			return;
	}

	DEBUG_BYTECODE_2("RMOVV", reg, var);
}

void AdderBytecodeEngine::RMOVA	(int reg, int var, float index)
{
	BREAK_CHECK_3("RMOVA", reg, var, index);

	if (this->_register_exists(reg))
	{
		THROW_ERROR_3("RMOVA", reg, var, index, "Register '" << reg << "' is already defined, therefore, can not copy value to this register.");
		return;
	}

	if (!this->_variable_exists(var))
	{
		THROW_ERROR_3("RMOVA", reg, var, index, "Undefined variable '" << var << "'.");
		return;
	}

	AdderValue * v = this->_variable_get(var);
	AdderValue * r = this->_register_new(reg);

	// Check and make sure we have valid values.
	assert(v != NULL && r != NULL);

	// Check to see if the variable is an array.
	if (v->type != AdderValueType::ARRAY)
	{
		THROW_ERROR_3("RMOVA", reg, var, index, "Variable '" << var << "' is not of array type.");
		return;
	}

	AdderValue * a = NULL;

	// Check the index exists.
	if (v->a_array()->find(index) != v->a_array()->end())
	{
		// Overwrite.
		AdderValue * a = (*v->a_array())[index];
	}
	else
	{
		// Too large.
		THROW_ERROR_3("RMOVA", reg, var, index, "Index " << index << " is larger than length of '" << var << "'.");
		return;
	}

	// Check and make sure we have valid values.
	assert(a != NULL);

	// Set the registers's type.
	r->type = a->type;
	
	switch (a->type)
	{
		case AdderValueType::NUMBER:
			r->r_number = a->r_number;
			break;
		case AdderValueType::STRING:
			r->a_string() = a->a_string().c_str();
			break;
		case AdderValueType::ARRAY:
			r->a_array() = a->a_array();
			break;
		case AdderValueType::OBJECT:
			r->r_object = a->r_object;
			break;
		default:
			THROW_ERROR_3("RMOVA", reg, var, index, "Type of variable '" << var << "' is unknown.");
			return;
	}

	DEBUG_BYTECODE_3("RMOVA", reg, var, index);
}

void AdderBytecodeEngine::RMOVP	(int reg, const char* prop, int object)
{
	BREAK_CHECK_3("RMOVP", reg, prop, object);

	// Not implemented.

	DEBUG_BYTECODE_3("RMOVP", reg, prop, object);
}

void AdderBytecodeEngine::MOVV	(int reg, int var)
{
	BREAK_CHECK_2("MOVV", reg, var);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("MOVV", reg, var, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * v = NULL;
	AdderValue * r = NULL;

	if (this->_variable_exists(var))
	{
		v = this->_variable_get(var);
		r = this->_register_get(reg);
	}
	else
	{
		v = this->_variable_new(var);
		r = this->_register_get(reg);
	}

	// Check and make sure we have valid values.
	assert(v != NULL && r != NULL);

	// Set the variable's type.
	v->type = r->type;
	
	switch (r->type)
	{
		case AdderValueType::NUMBER:
			v->r_number = r->r_number;
			break;
		case AdderValueType::STRING:
			v->a_string() = r->a_string().c_str();
			break;
		case AdderValueType::ARRAY:
			v->a_array() = r->a_array();
			break;
		case AdderValueType::OBJECT:
			v->r_object = r->r_object;
			break;
		default:
			THROW_ERROR_2("MOVV", reg, var, "Type of register '" << reg << "' is unknown.");
			return;
	}

	DEBUG_BYTECODE_2("MOVV", reg, var);
}

void AdderBytecodeEngine::MOVA	(int reg, int var, float index)
{
	BREAK_CHECK_3("MOVA", reg, var, index);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_3("MOVA", reg, var, index, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * v = NULL;
	AdderValue * r = NULL;

	if (this->_variable_exists(var))
	{
		v = this->_variable_get(var);
		r = this->_register_get(reg);
	}
	else
	{
		v = this->_variable_new(var);
		r = this->_register_get(reg);
	}

	// Check and make sure we have valid values.
	assert(v != NULL && r != NULL);

	AdderValue * a = NULL;

	// Now we need to check the variable is an array
	if (v->type == AdderValueType::ARRAY)
	{
		// Check the index exists.
		if (v->a_array()->find(index) != v->a_array()->end())
		{
			// Overwrite.
			a = (*v->a_array())[index];
		}
		else if (index == v->a_array()->size())
		{
			// Append.
			a = new AdderValue();
			v->a_array()->insert(float_value_map(index, a));
		}
		else
		{
			// Too large.
			THROW_ERROR_3("MOVA", reg, var, index, "Index " << index << " is larger than length of '" << var << "'.");
			return;
		}
	}
	else
	{
		// Not an array.
		THROW_ERROR_3("MOVA", reg, var, index, "Variable '" << var << "' is not of array type.");
		return;
	}

	// Set the variable's type.
	a->type = r->type;
	
	switch (r->type)
	{
		case AdderValueType::NUMBER:
			a->r_number = r->r_number;
			break;
		case AdderValueType::STRING:
			a->a_string() = r->a_string().c_str();
			break;
		case AdderValueType::ARRAY:
			a->a_array() = r->a_array();
			break;
		case AdderValueType::OBJECT:
			a->r_object = r->r_object;
			break;
		default:
			THROW_ERROR_3("MOVA", reg, var, index, "Type of register '" << reg << "' is unknown.");
			return;
	}

	DEBUG_BYTECODE_3("MOVA", reg, var, index);
}

void AdderBytecodeEngine::MOVP	(int reg, const char* prop, int object)
{
	BREAK_CHECK_3("MOVP", reg, prop, object);

	// Not implemented.

	DEBUG_BYTECODE_3("MOVP", reg, prop, object);
}

void AdderBytecodeEngine::DMOVV	(int reg, int reg2)
{
	BREAK_CHECK_2("DMOVV", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("DMOVV", reg, reg2, "Undefined variable '" << reg << "'.");
		return;
	}

	if (this->_register_exists(reg2))
	{
		THROW_ERROR_2("DMOVV", reg, reg2, "Register '" << reg2 << "' is already defined, therefore, can not copy value to this register.");
		return;
	}

	AdderValue * r = this->_register_get(reg);
	AdderValue * n = this->_register_new(reg2);

	// Check and make sure we have valid values.
	assert(r != NULL && n != NULL);

	// Set the registers's type.
	n->type = r->type;
	
	switch (r->type)
	{
		case AdderValueType::NUMBER:
			n->r_number = r->r_number;
			break;
		case AdderValueType::STRING:
			n->a_string() = r->a_string().c_str();
			break;
		case AdderValueType::ARRAY:
			n->a_array() = r->a_array();
			break;
		case AdderValueType::OBJECT:
			n->r_object = r->r_object;
			break;
		default:
			THROW_ERROR_2("DMOVV", reg, reg2, "Type of variable '" << reg << "' is unknown.");
			return;
	}

	DEBUG_BYTECODE_2("DMOVV", reg, reg2);
}

void AdderBytecodeEngine::DMOVA	(int reg, int reg2, float index)
{
	BREAK_CHECK_3("DMOVA", reg, reg2, index);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_3("DMOVA", reg, reg2, index, "Undefined variable '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_3("DMOVA", reg, reg2, index, "Undefined register '" << reg2 << "'.");
		return;
	}

	AdderValue * r = this->_register_get(reg);
	AdderValue * r2 = this->_register_get(reg2);
	AdderValue * a = NULL;

	// Check and make sure we have valid values.
	assert(r != NULL && r2 != NULL);

	// Now we need to check register 2 is an array
	if (r2->type == AdderValueType::ARRAY)
	{
		// Check the index exists.
		if (r2->a_array()->find(index) != r2->a_array()->end())
		{
			// Overwrite.
			a = (*r2->a_array())[index];
		}
		else if (index == r2->a_array()->size())
		{
			// Append.
			a = new AdderValue();
			r2->a_array()->insert(float_value_map(index, a));
		}
		else
		{
			// Too large.
			THROW_ERROR_3("DMOVA", reg, reg2, index, "Index " << index << " is larger than length of '" << reg2 << "'.");
			return;
		}
	}
	else
	{
		// Not an array.
		THROW_ERROR_3("DMOVA", reg, reg2, index, "Register '" << reg2 << "' is not of array type.");
		return;
	}

	// Check and make sure we have valid values.
	assert(a != NULL);

	// Set the registers's type.
	a->type = r->type;
	
	switch (r->type)
	{
		case AdderValueType::NUMBER:
			a->r_number = r->r_number;
			break;
		case AdderValueType::STRING:
			a->a_string() = r->a_string().c_str();
			break;
		case AdderValueType::ARRAY:
			a->a_array() = r->a_array();
			break;
		case AdderValueType::OBJECT:
			a->r_object = r->r_object;
			break;
		default:
			THROW_ERROR_3("DMOVA", reg, reg2, index, "Type of variable '" << reg << "' is unknown.");
			return;
	}

	DEBUG_BYTECODE_3("DMOVA", reg, reg2, index);
}

void AdderBytecodeEngine::DDMOVA	(int reg, int reg2, int reg3)
{
	BREAK_CHECK_3("DDMOVA", reg, reg2, reg3);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_3("DDMOVA", reg, reg2, reg3, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_3("DDMOVA", reg, reg2, reg3, "Undefined register '" << reg2 << "'.");
		return;
	}

	if (!this->_register_exists(reg3))
	{
		THROW_ERROR_3("DDMOVA", reg, reg2, reg3, "Undefined register '" << reg3 << "'.");
		return;
	}

	AdderValue * r = this->_register_get(reg);
	AdderValue * r2 = this->_register_get(reg2);
	AdderValue * i = this->_register_get(reg3);
	AdderValue * a = NULL;

	// Check and make sure we have valid values.
	assert(r != NULL && r2 != NULL && i != NULL);

	// Now we need to check register 2 is an array
	if (r2->type == AdderValueType::ARRAY)
	{
		// Check that the index register is a numeric.
		if (i->type != AdderValueType::NUMBER)
		{
			// Too large.
			THROW_ERROR_3("DDMOVA", reg, reg2, reg3, "Register '" << reg3 << "' is not a numeric value and therefore can not be used as an array index.");
			return;
		}

		// Check the index exists.
		if (r2->a_array()->find(i->r_number) != r2->a_array()->end())
		{
			// Overwrite.
			a = (*r2->a_array())[i->r_number];
		}
		else if (i->r_number == r2->a_array()->size())
		{
			// Append.
			a = new AdderValue();
			r2->a_array()->insert(float_value_map(i->r_number, a));
		}
		else
		{
			// Too large.
			THROW_ERROR_3("DDMOVA", reg, reg2, reg3, "Index " << i->r_number << " is larger than length of '" << reg2 << "'.");
			return;
		}
	}
	else
	{
		// Not an array.
		THROW_ERROR_3("DDMOVA", reg, reg2, reg3, "Register '" << reg2 << "' is not of array type.");
		return;
	}

	// Check and make sure we have valid values.
	assert(a != NULL);

	// Set the variable's type.
	a->type = r->type;
	
	switch (r->type)
	{
		case AdderValueType::NUMBER:
			a->r_number = r->r_number;
			break;
		case AdderValueType::STRING:
			a->a_string() = r->a_string().c_str();
			break;
		case AdderValueType::ARRAY:
			a->a_array() = r->a_array();
			break;
		case AdderValueType::OBJECT:
			a->r_object = r->r_object;
			break;
		default:
			THROW_ERROR_3("DDMOVA", reg, reg2, reg3, "Type of register '" << reg << "' is unknown.");
			return;
	}

	DEBUG_BYTECODE_3("DDMOVA", reg, reg2, reg3);
}

void AdderBytecodeEngine::DRMOVA	(int reg, int reg2, float index)
{
	BREAK_CHECK_3("DRMOVA", reg, reg2, index);

	// Not implemented.

	DEBUG_BYTECODE_3("DRMOVA", reg, reg2, index);
}

void AdderBytecodeEngine::DRMOVP	(int reg, const char* prop, int reg2)
{
	BREAK_CHECK_3("DRMOVP", reg, prop, reg2);

	// Not implemented.

	DEBUG_BYTECODE_3("DRMOVP", reg, prop, reg2);
}

void AdderBytecodeEngine::DDRMOVA	(int reg, int reg2, int reg3)
{
	BREAK_CHECK_3("DDRMOVA", reg, reg2, reg3);

	// Not implemented.

	DEBUG_BYTECODE_3("DDRMOVA", reg, reg2, reg3);
}

void AdderBytecodeEngine::REFA	(int reg, int var, float index)
{
	BREAK_CHECK_3("REFA", reg, var, index);

	// Not implemented.

	DEBUG_BYTECODE_3("REFA", reg, var, index);
}

void AdderBytecodeEngine::REFP	(int reg, const char* prop, int object)
{
	BREAK_CHECK_3("REFP", reg, prop, object);

	// Not implemented.

	DEBUG_BYTECODE_3("REFP", reg, prop, object);
}

void AdderBytecodeEngine::CMPV	(int res, int reg, const char* str)
{
	BREAK_CHECK_3("CMPV", res, reg, str);

	// Not implemented.

	DEBUG_BYTECODE_3("CMPV", res, reg, str);
}

void AdderBytecodeEngine::CMPV	(int res, int reg, float number)
{
	BREAK_CHECK_3("CMPV", res, reg, number);

	// Not implemented.

	DEBUG_BYTECODE_3("CMPV", res, reg, number);
}

void AdderBytecodeEngine::CMPR	(int res, int reg, int reg2)
{
	BREAK_CHECK_3("CMPR", res, reg, reg2);

	// Not implemented.

	DEBUG_BYTECODE_3("CMPR", res, reg, reg2);
}


// Bytecode: Breaks
void AdderBytecodeEngine::BVS		(const char* label, int var)
{
	BREAK_CHECK_2("BVS", label, var);

	if (this->_variable_exists(var))
	{
		this->breaking = true;
		this->breaking_label = label;
	}

	DEBUG_BYTECODE_2("BVS", label, var);
}

void AdderBytecodeEngine::BVNS	(const char* label, int var)
{
	BREAK_CHECK_2("BVNS", label, var);

	if (!this->_variable_exists(var))
	{
		this->breaking = true;
		this->breaking_label = label;
	}

	DEBUG_BYTECODE_2("BVNS", label, var);
}

void AdderBytecodeEngine::BRS		(const char* label, int reg)
{
	BREAK_CHECK_2("BRS", label, reg);

	if (this->_register_exists(reg))
	{
		this->breaking = true;
		this->breaking_label = label;
	}

	DEBUG_BYTECODE_2("BRS", label, reg);
}

void AdderBytecodeEngine::BRNS	(const char* label, int reg)
{
	BREAK_CHECK_2("BRNS", label, reg);

	if (!this->_register_exists(reg))
	{
		this->breaking = true;
		this->breaking_label = label;
	}

	DEBUG_BYTECODE_2("BRNS", label, reg);
}

void AdderBytecodeEngine::BT		(const char* label, int reg)
{
	BREAK_CHECK_2("BT", label, reg);

	if (this->_register_exists(reg))
	{
		AdderValue * r = this->_register_get(reg);

		if (r->type == AdderValueType::NUMBER)
		{
			if (r->r_number == 1.0f)
			{
				this->breaking = true;
				this->breaking_label = label;
			}
			else
			{
				this->breaking = false;
			}
		}
		else
		{
			// Only numerical values can be evaluated by BT and BF.
			this->breaking = false;
		}
	}
	else
	{
		THROW_ERROR_2("BT", label, reg, "Undefined register '" << reg << "'.");
		return;
	}

	DEBUG_BYTECODE_2("BT", label, reg);
}

void AdderBytecodeEngine::BF		(const char* label, int reg)
{
	BREAK_CHECK_2("BF", label, reg);

	if (this->_register_exists(reg))
	{
		AdderValue * r = this->_register_get(reg);

		if (r->type == AdderValueType::NUMBER)
		{
			if (r->r_number == 0.0f)
			{
				this->breaking = true;
				this->breaking_label = label;
			}
			else
			{
				this->breaking = false;
			}
		}
		else
		{
			// Only numerical values can be evaluated by BT and BF.
			this->breaking = false;
		}
	}
	else
	{
		THROW_ERROR_2("BF", label, reg, "Undefined register '" << reg << "'.");
		return;
	}

	DEBUG_BYTECODE_2("BF", label, reg);
}

void AdderBytecodeEngine::BU		(const char* label)
{
	BREAK_CHECK_1("BU", label);

	this->breaking = true;
	this->breaking_label = label;

	DEBUG_BYTECODE_1("BU", label);
}

void AdderBytecodeEngine::BERR	(const char* label, int reg,  int reg2)
{
	BREAK_CHECK_3("BERR", label, reg, reg2);

	// Not implemented.

	DEBUG_BYTECODE_3("BERR", label, reg, reg2);
}

void AdderBytecodeEngine::BRK		(const char* label)
{
	if (strcmp(this->breaking_label,label) == 0 ||
		(this->breaking_catching && strcmp(this->breaking_catchlabel,label) == 0))
	{
		if (this->breaking_catching)
		{
			this->registers->clear();
			AdderValue * c = this->_register_new(this->breaking_catchregister);
			c->type = AdderValueType::OBJECT;
			c->r_object = this->breaking_catchexcp;
		}

		this->breaking = false;
		this->breaking_catching = false;
		this->breaking_label = "";
		this->breaking_catchlabel = "";

		DEBUG_BYTECODE_1("BRK", label);
	}
	else
	{
		// We want to indicate that the engine skipped this
		// BRK command.
		BREAK_CHECK_1("BRK", label);
	}
}


// Bytecode: Register / Variable Cleanup
void AdderBytecodeEngine::CLR		(int reg)
{
	BREAK_CHECK_1("CLR", reg);

	if (this->_register_exists(reg))
	{
		this->_register_clean(reg);
	}

	DEBUG_BYTECODE_1("CLR", reg);
}

void AdderBytecodeEngine::CLRV	(int var)
{
	BREAK_CHECK_1("CLRV", var);

	if (this->_variable_exists(var))
	{
		this->_variable_del(var);
	}

	DEBUG_BYTECODE_1("CLRV", var);
}

// Bytecode: Labels / Jumps
void AdderBytecodeEngine::LBL		(const char* label)
{
	BREAK_CHECK_1("LBL", label);

	// Not implemented.

	DEBUG_BYTECODE_1("LBL", label);
}
void AdderBytecodeEngine::JMPR	(float position)
{
	BREAK_CHECK_1("JMPR", position);

	// Not implemented.

	DEBUG_BYTECODE_1("JMPR", position);
}
void AdderBytecodeEngine::JMPA	(float position)
{
	BREAK_CHECK_1("JMPA", position);

	// Not implemented.

	DEBUG_BYTECODE_1("JMPA", position);
}
void AdderBytecodeEngine::JMPL	(const char* label)
{
	BREAK_CHECK_1("JMPL", label);

	// Not implemented.

	DEBUG_BYTECODE_1("JMPL", label);
}

// Bytecode: Return System
void AdderBytecodeEngine::RET		(int reg)
{
	BREAK_CHECK_1("RET", reg);

	// Not implemented.

	DEBUG_BYTECODE_1("RET", reg);
}

// Bytecode: Arithmetic Functions
void AdderBytecodeEngine::ADD		(int reg, const char* str)
{
	BREAK_CHECK_2("ADD", reg, str);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("ADD", reg, str, "Undefined register '" << reg << "'.");
		return;
	}

	AdderString reg2_str = str;
	AdderValue * b = NULL;
	bool is_string_literal = false;

	if (reg2_str.at(0) == '"' && reg2_str.at(reg2_str.size()-1) == '"')
	{
		// We're dealing with a string literal.
		b = new AdderValue();
		b->type = AdderValueType::STRING;
		b->a_string() = reg2_str.substr(1, reg2_str.size() - 2).get();
		is_string_literal = true;
	}
	else
	{
		THROW_ERROR_2("ADD", reg, str, "'" << str << "' is not a valid string literal.");
		return;
	}

	AdderValue * a = this->_register_get(reg);

	// Check that b is valid.
	assert(b != NULL);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		delete b;
		THROW_ERROR_2("ADD", reg, str, "'" << reg << "' must be a string register in order to perform concatenation.");
		return;
	}
	else if (a->type == AdderValueType::STRING)
	{
		// Concatenation operation.
		if (b->type != AdderValueType::STRING)
		{
			delete b;
			THROW_ERROR_2("ADD", reg, str, "'" << str << "' is not a string register nor a valid literal string value.");
			return;
		}

		AdderString v;
		v << a->a_string() << b->a_string();
		a->a_string() = v.c_str();
	}
	else
	{
		// Unknown operation.
		delete b;
		THROW_ERROR_2("ADD", reg, str, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	delete b;

	DEBUG_BYTECODE_2("ADD", reg, str);
}

void AdderBytecodeEngine::ADD		(int reg, int reg2)
{
	BREAK_CHECK_2("ADD", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("ADD", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	// Check that b is valid.
	assert(b != NULL);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		if (b->type != AdderValueType::NUMBER)
		{
			THROW_ERROR_2("ADD", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
			return;
		}

		a->r_number += b->r_number;
	}
	else if (a->type == AdderValueType::STRING)
	{
		// Concatenation operation.
		if (b->type != AdderValueType::STRING)
		{
			THROW_ERROR_2("ADD", reg, reg2, "'" << reg2 << "' is not a string register nor a valid literal string value.");
			return;
		}

		AdderString v;
		v << a->a_string() << b->a_string();
		a->a_string() = v.c_str();
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("ADD", reg, reg2, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("ADD", reg, reg2);
}

void AdderBytecodeEngine::ADD		(int reg, float value)
{
	BREAK_CHECK_2("ADD", reg, value);

	if (!_register_exists(reg))
	{
		THROW_ERROR_2("ADD", reg, value, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * a = _register_get(reg);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		a->r_number += value;
	}
	else if (a->type == AdderValueType::STRING)
	{
		// Concatenation operation.
		THROW_ERROR_2("ADD", reg, value, "'" << value << "' is not a string register nor a valid literal string value.");
		return;
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("ADD", reg, value, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("ADD", reg, value);
}

void AdderBytecodeEngine::SUB		(int reg, int reg2)
{
	BREAK_CHECK_2("SUB", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("SUB", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("SUB", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		if (b->type != AdderValueType::NUMBER)
		{
			THROW_ERROR_2("SUB", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
			return;
		}

		a->r_number -= b->r_number;
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("SUB", reg, reg2, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("SUB", reg, reg2);
}

void AdderBytecodeEngine::SUB		(int reg, float value)
{
	BREAK_CHECK_2("SUB", reg, value);

	if (!_register_exists(reg))
	{
		THROW_ERROR_2("SUB", reg, value, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * a = _register_get(reg);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		a->r_number -= value;
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("SUB", reg, value, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("SUB", reg, value);
}

void AdderBytecodeEngine::MUL		(int reg, int reg2)
{
	BREAK_CHECK_2("MUL", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("MUL", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("MUL", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		if (b->type != AdderValueType::NUMBER)
		{
			THROW_ERROR_2("MUL", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
			return;
		}

		a->r_number *= b->r_number;
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("MUL", reg, reg2, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("MUL", reg, reg2);
}

void AdderBytecodeEngine::MUL		(int reg, float value)
{
	BREAK_CHECK_2("MUL", reg, value);

	if (!_register_exists(reg))
	{
		THROW_ERROR_2("MUL", reg, value, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * a = _register_get(reg);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		a->r_number *= value;
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("MUL", reg, value, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("MUL", reg, value);
}

void AdderBytecodeEngine::DIV		(int reg, int reg2)
{
	BREAK_CHECK_2("DIV", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("DIV", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("DIV", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		if (b->type != AdderValueType::NUMBER)
		{
			THROW_ERROR_2("DIV", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
			return;
		}

		a->r_number /= b->r_number;
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("DIV", reg, reg2, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("DIV", reg, reg2);
}

void AdderBytecodeEngine::DIV		(int reg, float value)
{
	BREAK_CHECK_2("DIV", reg, value);

	if (!_register_exists(reg))
	{
		THROW_ERROR_2("DIV", reg, value, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * a = _register_get(reg);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		a->r_number /= value;
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("DIV", reg, value, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("DIV", reg, value);
}

void AdderBytecodeEngine::POW		(int reg, int reg2)
{
	BREAK_CHECK_2("POW", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("POW", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("POW", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		if (b->type != AdderValueType::NUMBER)
		{
			THROW_ERROR_2("POW", reg, reg2, "'" << reg2 << "' is not a numeric register nor a valid numeric value.");
			return;
		}

		a->r_number = pow(a->r_number,b->r_number);
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("POW", reg, reg2, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("POW", reg, reg2);
}

void AdderBytecodeEngine::POW		(int reg, float value)
{
	BREAK_CHECK_2("POW", reg, value);

	if (!_register_exists(reg))
	{
		THROW_ERROR_2("POW", reg, value, "Undefined register '" << reg << "'.");
		return;
	}

	AdderValue * a = _register_get(reg);

	if (a->type == AdderValueType::NUMBER)
	{
		// Add operation.
		a->r_number = pow(a->r_number,value);
	}
	else
	{
		// Unknown operation.
		THROW_ERROR_2("POW", reg, value, "The register '" << reg << "' is not a valid type for the requested operation.");
	}

	DEBUG_BYTECODE_2("POW", reg, value);
}

// Bytecode: Arithmetic Comparisons
void AdderBytecodeEngine::EQ		(int reg, int reg2)
{
	BREAK_CHECK_2("EQ", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("EQ", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("EQ", reg, reg2, "Undefined register '" << reg2 << "'.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER && b->type == AdderValueType::NUMBER)
	{
		if (a->r_number == b->r_number)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;
	}
	else if (a->type == AdderValueType::STRING && b->type == AdderValueType::STRING)
	{
		a->type = AdderValueType::NUMBER;
		if (a->a_string() == b->a_string())
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;

		// Cleanup
		a->a_string() = "";
	}
	else if (a->type == AdderValueType::ARRAY && b->type == AdderValueType::ARRAY)
	{
		a->type = AdderValueType::NUMBER;
		a->r_number = 0.0f;

		if (this->arraysAreEqual(a->a_array(), b->a_array()))
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;

		// Cleanup
		a->a_array()->clear();
	}
	else if (a->type == AdderValueType::OBJECT && b->type == AdderValueType::OBJECT)
	{
		a->type = AdderValueType::NUMBER;
		if (a->r_object == b->r_object)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;

		// Cleanup
		a->r_object = NULL;
	}
	else
	{
		THROW_ERROR_2("EQ", reg, reg2, "The equality test (equal to) on '" << reg << "' with value '" << reg2 << "' could not be completed: type mismatch.");
	}

	DEBUG_BYTECODE_2("EQ", reg, reg2);
}

void AdderBytecodeEngine::NE		(int reg, int reg2)
{
	BREAK_CHECK_2("NE", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("NE", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("NE", reg, reg2, "Undefined register '" << reg2 << "'.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER && b->type == AdderValueType::NUMBER)
	{
		if (a->r_number != b->r_number)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;
	}
	else if (a->type == AdderValueType::STRING && b->type == AdderValueType::STRING)
	{
		a->type = AdderValueType::NUMBER;
		if (a->a_string() != b->a_string())
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;

		// Cleanup
		a->a_string() = "";
	}
	else if (a->type == AdderValueType::ARRAY && b->type == AdderValueType::ARRAY)
	{
		a->type = AdderValueType::NUMBER;
		a->r_number = 0.0f;

		if (!this->arraysAreEqual(a->a_array(), b->a_array()))
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;

		// Cleanup
		a->a_array()->clear();
	}
	else if (a->type == AdderValueType::OBJECT && b->type == AdderValueType::OBJECT)
	{
		a->type = AdderValueType::NUMBER;
		if (a->r_object != b->r_object)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;

		// Cleanup
		a->r_object = NULL;
	}
	else
	{
		THROW_ERROR_2("NE", reg, reg2, "The equality test (not equal to) on '" << reg << "' with value '" << reg2 << "' could not be completed: type mismatch.");
	}

	DEBUG_BYTECODE_2("NE", reg, reg2);
}

void AdderBytecodeEngine::LT		(int reg, int reg2)
{
	BREAK_CHECK_2("LT", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("LT", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("LT", reg, reg2, "Undefined register '" << reg2 << "'.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER && b->type == AdderValueType::NUMBER)
	{
		if (a->r_number < b->r_number)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;
	}
	else
	{
		THROW_ERROR_2("LT", reg, reg2, "The equality test (less than) on '" << reg << "' with value '" << reg2 << "' could not be completed: type mismatch or type not supported.");
	}

	DEBUG_BYTECODE_2("LT", reg, reg2);
}

void AdderBytecodeEngine::LE		(int reg, int reg2)
{
	BREAK_CHECK_2("LE", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("LE", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("LE", reg, reg2, "Undefined register '" << reg2 << "'.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER && b->type == AdderValueType::NUMBER)
	{
		if (a->r_number <= b->r_number)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;
	}
	else
	{
		THROW_ERROR_2("LE", reg, reg2, "The equality test (less than or equal to) on '" << reg << "' with value '" << reg2 << "' could not be completed: type mismatch or type not supported.");
	}

	DEBUG_BYTECODE_2("LE", reg, reg2);
}

void AdderBytecodeEngine::GT		(int reg, int reg2)
{
	BREAK_CHECK_2("GT", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("GT", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("GT", reg, reg2, "Undefined register '" << reg2 << "'.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER && b->type == AdderValueType::NUMBER)
	{
		if (a->r_number > b->r_number)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;
	}
	else
	{
		THROW_ERROR_2("GT", reg, reg2, "The equality test (greater than) on '" << reg << "' with value '" << reg2 << "' could not be completed: type mismatch or type not supported.");
	}

	DEBUG_BYTECODE_2("GT", reg, reg2);
}

void AdderBytecodeEngine::GE		(int reg, int reg2)
{
	BREAK_CHECK_2("GE", reg, reg2);

	if (!this->_register_exists(reg))
	{
		THROW_ERROR_2("GE", reg, reg2, "Undefined register '" << reg << "'.");
		return;
	}

	if (!this->_register_exists(reg2))
	{
		THROW_ERROR_2("GE", reg, reg2, "Undefined register '" << reg2 << "'.");
		return;
	}

	AdderValue * a = this->_register_get(reg);
	AdderValue * b = this->_register_get(reg2);

	if (a->type == AdderValueType::NUMBER && b->type == AdderValueType::NUMBER)
	{
		if (a->r_number >= b->r_number)
			a->r_number = 1.0f;
		else
			a->r_number = 0.0f;
	}
	else
	{
		THROW_ERROR_2("GE", reg, reg2, "The equality test (greater than or equal to) on '" << reg << "' with value '" << reg2 << "' could not be completed: type mismatch or type not supported.");
	}

	DEBUG_BYTECODE_2("GE", reg, reg2);
}

// Bytecode: Function Calling
void AdderBytecodeEngine::CALLF	(const char* func, int arglist, int ret)
{
	BREAK_CHECK_3("CALLF", func, arglist, ret);

	// This function has to determine whether the request function is a 
	// builtin function or whether it is a user-defined function.
	if (this->functions.find(AdderString(func)) != this->functions.end())
	{
		// Function was found.
		AdderFunction * funcR = this->functions[AdderString(func)];
		if (funcR->isBuiltin)
		{
			BuiltinFunction* funcB = static_cast<BuiltinFunction*>(funcR);

			// Check that the argument list exists.
			if (!this->_register_exists(arglist))
			{
				THROW_ERROR_3("CALLF", func, arglist, ret, "Register for argument list '" << arglist << "' not found.");
				return;
			}

			AdderValue * argumentList = this->_register_get(arglist);

			// Check that the argument list is an array.
			if (argumentList->type != AdderValueType::ARRAY)
			{
				THROW_ERROR_3("CALLF", func, arglist, ret, "Register for argument list '" << arglist << "' is not an array.");
				return;
			}

			// Check that the argument list length is the same as
			// the function's argument count.
			if (argumentList->a_array()->size() != funcB->argument_count)
			{
				THROW_ERROR_3("CALLF", func, arglist, ret, "Argument count mismatch while calling '" << func << "' (" << funcB->argument_count << " defined, " << argumentList->a_array()->size() << " provided)");
				return;
			}

			// Now call the builtin using the arguments provided.
			AdderValue * arg1 = NULL;
			AdderValue * arg2 = NULL;
			AdderValue * arg3 = NULL;
			AdderValue * arg4 = NULL;
			AdderValue * arg5 = NULL;
			if (funcB->argument_count >= 1) arg1 = (*argumentList->a_array())[0];
			if (funcB->argument_count >= 2) arg2 = (*argumentList->a_array())[1];
			if (funcB->argument_count >= 3) arg3 = (*argumentList->a_array())[2];
			if (funcB->argument_count >= 4) arg4 = (*argumentList->a_array())[3];
			if (funcB->argument_count >= 5) arg5 = (*argumentList->a_array())[4];

			AdderValue * returnValue = NULL;
			switch (funcB->argument_count)
			{
				case 0:
					returnValue = funcB->execute(this);
					break;
				case 1:
					returnValue = funcB->execute(this, arg1);
					break;
				case 2:
					returnValue = funcB->execute(this, arg2);
					break;
				case 3:
					returnValue = funcB->execute(this, arg3);
					break;
				case 4:
					returnValue = funcB->execute(this, arg4);
					break;
				case 5:
					returnValue = funcB->execute(this, arg5);
					break;
				default:
					THROW_ERROR_3("CALLF", func, arglist, ret, "Too many or invalid number of arguments for builtin function '" << func << "'");
					return;
			}

			// Now push the return value onto the
			// registers list if it isn't NULL.
			if (returnValue != NULL)
			{
				if (this->_register_exists(ret))
				{
					AdderValue * reg = this->_register_get(ret);
					reg = returnValue;
				}
				else
				{
					AdderValue * reg = this->_register_new(ret);
					reg = returnValue;
				}
				delete returnValue;
			}
			else
			{
				// If the function didn't return anything, we
				// don't set the register to anything.
			}
		}
		else
		{
			// Check that the argument list exists.
			if (!this->_register_exists(arglist))
			{
				THROW_ERROR_3("CALLF", func, arglist, ret, "Register for argument list '" << arglist << "' not found.");
				return;
			}

			AdderValue * argumentList = this->_register_get(arglist);

			// Check that the argument list is an array.
			if (argumentList->type != AdderValueType::ARRAY)
			{
				THROW_ERROR_3("CALLF", func, arglist, ret, "Register for argument list '" << arglist << "' is not an array.");
				return;
			}

			// Check that the argument list length is the same as
			// the function's argument count.
			//if (argumentList->a_array()->size() != funcR->argument_count)
			//{
			//	THROW_ERROR_3("CALLF", func, arglist, ret, "Argument count mismatch while calling '" << func << "' (" << funcR->argument_count << " defined, " << argumentList->a_array()->size() << " provided)");
			//	return;
			//}

			// We need to set up the appropriate scoping for the function call.
			// We use a global variable (in AdderBytecodeEngine) to temporarily
			// store the variable and register dictionaries at this level so that
			// we can restore them after the assembly has returned from the
			// actual user-defined function.
			this->variables->raise();
			this->registers->raise();
			this->scope_level += 1;

			// Reload all of the builtin variables.
			for ( std::map<AdderString, BuiltinScopeChangeFunction>::const_iterator iter = this->builtins_scope_reload_functions.begin();
			  iter != this->builtins_scope_reload_functions.end(); ++iter )
			{
				iter->second(this);
			}

			// Now load the arguments as variables.
			for (int i = 0; i < funcR->argument_names.size(); i += 1)
			{
				if ((*argumentList->a_array())[i] != NULL)
				{
					AdderValue * r = this->_variable_new(funcR->argument_names[i]);
					r = (*argumentList->a_array())[i];
				}
				else
				{
					// No argument.
				}
			}

			// The scope and variables are now set up.  We need to set our global
			// variable that indicates the assembly needs to jump.
			this->assembly_jump_required = true;
		}
	}
	else
	{
		THROW_ERROR_3("CALLF", func, arglist, ret, "Undefined function '" << func << "'");
	}

	DEBUG_BYTECODE_3("CALLF", func, arglist, ret);
}
void AdderBytecodeEngine::CALLC	(const char* func, int arglist, int ret, int object)
{
	BREAK_CHECK_4("CALLC", func, arglist, ret, object);

	// Not implemented.

	DEBUG_BYTECODE_4("CALLC", func, arglist, ret, object);
}

// Bytecode: Errors
void AdderBytecodeEngine::ERR		(float position, const char* msg)
{
	BREAK_CHECK_2("ERR", position, msg);

	// Throw an error (we won't know what position it occurred at though because it's native code).
	this->_throwError("<UNKNOWN>", msg);

	DEBUG_BYTECODE_2("ERR", position, msg);
}

/* Array Recursive Comparison */
bool AdderBytecodeEngine::arraysAreEqual(std::map<float, AdderValue*> * a, std::map<float, AdderValue*> * b, int level)
{
	// Loop through the elements in the first array and determine whether the arrays
	// are equal.
	if (level >= 1000)
	{
		// We've hit the array recursion limit.
		THROW_ERROR_0("<UNKNOWN>", "Array comparison recursion limit hit.");
		return false;
	}

	if (a->size() == b->size())
	{
		// Now check each individual element.
		for ( std::map<float, AdderValue*>::const_iterator iter = b->begin();
			iter != b->end(); ++iter )
		{
			// Check existance.
			if (a->find(iter->first) == a->end())
				return false;

			// Check types.
			if (iter->second->type != (*a)[iter->first]->type)
				return false;
			
			if (iter->second->type == AdderValueType::STRING)
			{
				if (iter->second->a_string() != (*a)[iter->first]->a_string())
					return false;
			}
			else if (iter->second->type == AdderValueType::NUMBER)
			{
				if (iter->second->r_number != (*a)[iter->first]->r_number)
					return false;
			}
			else if (iter->second->type == AdderValueType::ARRAY)
			{
				bool res = this->arraysAreEqual(iter->second->a_array(), (*a)[iter->first]->a_array(), level + 1);
				if (!res)
					return false;
			}
			else if (iter->second->type == AdderValueType::OBJECT)
			{
				if (iter->second->r_object != (*a)[iter->first]->r_object)
					return false;
			}
		}

		// We've completed the loop without failing
		// so they must be the same.
		return true;
	}
	else
		return false;
}

/* Register Functions */
AdderValue * AdderBytecodeEngine::_register_new(int reg)
{
	// Our overloaded [] operator automatically creates if needed.
	return (*this->registers)[reg];
}

void AdderBytecodeEngine::_register_del(int reg)
{
	this->registers->erase(reg);
}

void AdderBytecodeEngine::_register_clean(int reg)
{
	// Our overloaded [] operator automatically creates if needed.
	(*this->registers)[reg]->type = AdderValueType::UNSET;
}

bool AdderBytecodeEngine::_register_exists(int reg)
{
	return !(!this->registers->exists(reg) || (*this->registers)[reg]->type == AdderValueType::UNSET);
}

AdderValue * AdderBytecodeEngine::_register_get(int reg)
{
	return (*this->registers)[reg];
}

/*void AdderBytecodeEngine::_register_del(int reg)
{
	this->registers[reg] = NULL;
}

void AdderBytecodeEngine::_register_clean(int reg)
{
	this->registers[reg]->type = AdderValueType::UNSET;
	//this->registers[reg]->a_array()->clear();
	//this->registers[reg]->r_object = NULL;
}

bool AdderBytecodeEngine::_register_exists(int reg)
{
	if (reg >= this->registers.size()) return false;
	if (this->registers[reg] == NULL) return false;
	if (this->registers[reg]->type == AdderValueType::UNSET) return false;
	return true;
}

AdderValue * AdderBytecodeEngine::_register_get(int reg)
{
	return this->registers[reg];
}*/

/* Register Functions */
AdderValue * AdderBytecodeEngine::_variable_new(int var)
{
	// Our overloaded [] operator automatically creates if needed.
	return (*this->variables)[var];
}

void AdderBytecodeEngine::_variable_del(int var)
{
	this->variables->erase(var);
}

bool AdderBytecodeEngine::_variable_exists(int var)
{
	return this->variables->exists(var);
}

AdderValue * AdderBytecodeEngine::_variable_get(int var)
{
	return (*this->variables)[var];
}

/* Classes Functions */
AdderObject * AdderBytecodeEngine::_class_define(const char* cls)
{
	AdderObject * t = new AdderObject(this);
	this->classes.insert(string_class_map(AdderString(cls), t));
	return t;
}

void AdderBytecodeEngine::_class_undefine(const char* cls)
{
	this->classes.erase(AdderString(cls));
}

bool AdderBytecodeEngine::_class_defined(const char* cls)
{
	return (this->classes.find(cls) != this->classes.end());
}

AdderObject * AdderBytecodeEngine::_class_get(const char* cls)
{
	return this->classes[AdderString(cls)];
}

/* Debugging Functions */
void AdderBytecodeEngine::_breakDebug(const char* bytecode_text)
{
	if (!this->debug) return;

	// This function is called when the system
	// is currently breaking.

	std::cout
		<< "==========================================================" << std::endl
		<< "Debug Interrupt:" << std::endl
		<< "  File \"<native code>\", line <unknown>" << std::endl
		<< "    " << bytecode_text << std::endl
		<< std::endl
		<< "has just been skipped." << std::endl
		<< std::endl
		<< "Press enter to continue when ready." << std::endl
		<< "==========================================================" << std::endl;

	fflush ( stdout );
	getchar();
}

void AdderBytecodeEngine::_debug(const char* bytecode_text)
{
	if (!this->debug) return;

	// This function outputs the state of the
	// bytecode executor.

	std::cout
		<< "==========================================================" << std::endl
		<< "Debug Interrupt:" << std::endl
		<< "  File \"<native code>\", line <unknown>" << std::endl
		<< "    " << bytecode_text << std::endl
		<< std::endl
		<< "has just been executed.  The executor's current state is:" << std::endl
		<< std::endl
		<< "--- Variables ---" << std::endl;

	this->_debugValueMap(this->variables);

	std::cout
		<< "--- Register ---" << std::endl;

	this->_debugValueMap(this->registers);

	std::cout
		<< "--- Result Container ---" << std::endl;

	this->_debugValue(this->return_scope);

	std::cout
		<< std::endl
		<< "Press enter to continue when ready." << std::endl
		<< "==========================================================" << std::endl;

	fflush ( stdout );
	getchar();
}

void AdderBytecodeEngine::_debugMsg(const char *msg)
{
	printf("[Adder NE] %s\n", msg);
}

const char* AdderBytecodeEngine::_debugGetTypeString(AdderValueType t)
{
	switch (t)
	{
		case AdderValueType::NUMBER:
			return "number";
			break;
		case AdderValueType::ARRAY:
			return "array";
			break;
		case AdderValueType::STRING:
			return "string";
			break;
		case AdderValueType::OBJECT:
			return "object";
			break;
		default:
			return "unknown";
			break;
	}
}

void AdderBytecodeEngine::_debugValue(AdderValue* m)
{
	if (m == NULL)
	{
		printf("Empty\n");
		return;
	}

	const char* t = _debugGetTypeString(m->type);
	if (m->type == AdderValueType::STRING)
		printf("%s\n", m->a_string().c_str());
	else if (m->type == AdderValueType::NUMBER)
		printf("%f\n", m->r_number);
	else
		printf("(%s)\n", t);
}

void AdderBytecodeEngine::_debugValueMap(AdderVariableScoper * m, int indent)
{
	int a = 0;

	if (indent >= 1000 * 2)
	{
		// We've hit the array recursion limit.
		THROW_ERROR_0("<UNKNOWN>", "Array comparison recursion limit hit.");
		return;
	}

	if (m->size() == 0)
	{
		printf("%sEmpty\n", this->_indentPad(indent).c_str());
		return;
	}

	for (int i = 0; i < m->size(); i ++)
	{
		if ((*m)[i] == NULL)
		{
			a++;
			continue;
		}
		const char* t = _debugGetTypeString((*m)[i]->type);
		if ((*m)[i]->type == AdderValueType::STRING)
			printf("%s%i: (%s) = %s\n", this->_indentPad(indent).c_str(), a, t, (*m)[i]->a_string().c_str());
		else if ((*m)[i]->type == AdderValueType::NUMBER)
			printf("%s%i: (%s) = %f\n", this->_indentPad(indent).c_str(), a, t, (*m)[i]->r_number);
		else if ((*m)[i]->type == AdderValueType::ARRAY)
		{
			printf("%s%i: (%s)\n", this->_indentPad(indent).c_str(), a, t);
			this->_debugArrayMap((*m)[i]->a_array(), indent + 2);
		}
		else
			printf("%s%i: (%s)\n", this->_indentPad(indent).c_str(), a, t);
		a++;
	}
}

void AdderBytecodeEngine::_debugValueMap(std::vector<AdderValue*> m, int indent)
{
	int a = 0;

	if (indent >= 1000 * 2)
	{
		// We've hit the array recursion limit.
		THROW_ERROR_0("<UNKNOWN>", "Array comparison recursion limit hit.");
		return;
	}

	if (m.size() == 0)
	{
		printf("%sEmpty\n", this->_indentPad(indent).c_str());
		return;
	}

	for (int i = 0; i < m.size(); i ++)
	{
		if (m[i] == NULL)
		{
			a++;
			continue;
		}
		const char* t = _debugGetTypeString(m[i]->type);
		if (m[i]->type == AdderValueType::STRING)
			printf("%s%i: (%s) = %s\n", this->_indentPad(indent).c_str(), a, t, m[i]->a_string().c_str());
		else if (m[i]->type == AdderValueType::NUMBER)
			printf("%s%i: (%s) = %f\n", this->_indentPad(indent).c_str(), a, t, m[i]->r_number);
		else if (m[i]->type == AdderValueType::ARRAY)
		{
			printf("%s%i: (%s)\n", this->_indentPad(indent).c_str(), a, t);
			this->_debugArrayMap(m[i]->a_array(), indent + 2);
		}
		else
			printf("%s%i: (%s)\n", this->_indentPad(indent).c_str(), a, t);
		a++;
	}
}

void AdderBytecodeEngine::_debugArrayMap(std::map<float, AdderValue*> * m, int indent)
{
	if (indent >= 1000 * 2)
	{
		// We've hit the array recursion limit.
		THROW_ERROR_0("<UNKNOWN>", "Array comparison recursion limit hit.");
		return;
	}

	if (m->size() == 0)
	{
		return;
	}

	for ( std::map<float, AdderValue*>::const_iterator iter = m->begin();
      iter != m->end(); ++iter )
	{
		const char* t = _debugGetTypeString(iter->second->type);
		if (iter->second->type == AdderValueType::STRING)
			printf("%s%i: (%s) = %s\n", this->_indentPad(indent).c_str(), (int)iter->first, t, iter->second->a_string().c_str());
		else if (iter->second->type == AdderValueType::NUMBER)
			printf("%s%i: (%s) = %f\n", this->_indentPad(indent).c_str(), (int)iter->first, t, iter->second->r_number);
		else if (iter->second->type == AdderValueType::ARRAY)
		{
			printf("%s%i: (%s)\n", this->_indentPad(indent).c_str(), (int)iter->first, t);
			this->_debugArrayMap(iter->second->a_array(), indent + 2);
		}
		else
			printf("%s%i: (%s)\n", this->_indentPad(indent).c_str(), (int)iter->first, t);
	}
}

void AdderBytecodeEngine::_debugObjectMap(std::map<AdderString, AdderObject*> m, int indent)
{
	// Not implemented.
}

void AdderBytecodeEngine::_debugFunctionMap(std::map<AdderString, AdderFunction*> m, int indent)
{
	// Not implemented.
}

AdderString AdderBytecodeEngine::_indentPad(int indent)
{
	assert(indent >= 0 && indent <= 2000);

	AdderString pad;
	for (int i = 0; i < indent; i += 1)
		pad << " ";
	return pad;
}

/* Error / Exception Functions */
void AdderBytecodeEngine::_throwError(const char* bytecode_text, const char* error_text)
{
	// This function will cause the application to exit.
	std::cout 
		<< "Traceback (most recent call last):" << std::endl
		<< "  File \"<native code>\", line <unknown>" << std::endl
		<< "    " << bytecode_text << std::endl
		<< std::endl
		<< "RuntimeError: " << error_text << std::endl
		<< std::endl;
}